/*---------------------------------------------------------------------
 * File name: ds18b20.cpp
 *
 * Copyright (c) <2020-2022>, <ChenLong>
 *
 * All rights reserved.
 *
 * Author: ChenLong
 * Email: worldlong@foxmail.com
 *--------------------------------------------------------------------*/
#include "ds18b20.h"
#include "hal_system.h"
/*
*/
void DS18B20::dq_out_high()
{
  if(_dq_out_reverse) {
    _dq_out->reset();
  } else {
    _dq_out->set();
  }
}
/*
*/
void DS18B20::dq_out_low()
{
  if(_dq_out_reverse) {
    _dq_out->set();
  } else {
    _dq_out->reset();
  }
}
/*
*/
void DS18B20::write(uint8_t *buffer, int number)
{
  for(int i=0; i<number; i++) {
    uint32_t sr = enter_critical();
    for(int j=0; j<8; j++) {
      dq_out_low();
      _timer->delay_us(10);
      if((buffer[i] >> j) & 0x01) {
        dq_out_high();
      }
      _timer->delay_us(50);  //at least keep 60us
      dq_out_high();
      _timer->delay_us(5);
    }
    exit_critical(sr);
  }
  _timer->delay_us(20);
}
/*
*/
void DS18B20::read(uint8_t *buffer, int number)
{
  for(int i=0; i<number; i++) {
    buffer[i] = 0;
    uint32_t sr = enter_critical();
    for(int j=0; j<8; j++) {
      dq_out_low();
      _timer->delay_us(2);
      dq_out_high();
      _timer->delay_us(10);
      if(_dq_in->read()) {
        buffer[i] |= 1<<j;
      }
      _timer->delay_us(55);  //at least keep 60us
    }
    exit_critical(sr);
  }
  _timer->delay_us(20);
}
/*
*/
void DS18B20::set_rom(uint8_t *buffer) 
{
  memcpy(rom, buffer, 8);
}
/*
*/
bool DS18B20::init()
{
  dq_out_high();
  _timer->delay_us(10);
  return check();
}
/*
*/
bool DS18B20::reset()
{
  dq_out_low();
  _timer->delay_us(500);  //at least keep 480us
  dq_out_high();
  uint32_t sr = enter_critical();
  _timer->delay_us(100); 
  bool ret = !_dq_in->read();   //ack low pulse
  exit_critical(sr);
  _timer->delay_us(400);
  return ret;
}
/*
*/
void DS18B20::read_rom(uint8_t *buffer)
{
  if(reset()) {
    uint8_t cmd = DS18B20_CMD_READ_ROM;
    write(&cmd, 1);
    read(buffer, 8);
  }
}
/*
*/
void DS18B20::match_rom()
{
  if(reset()) {
    uint8_t cmd = DS18B20_CMD_MATCH_ROM;
    write(&cmd, 1);
    write(rom, 8);
  }
}
/*
*/
void DS18B20::skip_rom()
{
  if(reset()) {
    uint8_t cmd = DS18B20_CMD_SKIP_ROM;
    write(&cmd, 1);
  }
}
/*
*/
void DS18B20::read_scratchpad(uint8_t *buffer)
{
  match_rom();
  uint8_t cmd = DS18B20_CMD_READ_SCRATCHPAD;
  write(&cmd, 1);
  read(buffer, 9);
}
/*
*/
void DS18B20::write_scratchpad(uint8_t *buffer)
{
  match_rom();
  uint8_t cmd = DS18B20_CMD_WRITE_SCRATCHPAD;
  write(&cmd, 1);
  write(buffer, 5);
}
/*
*/
void DS18B20::start_convert(bool skip)
{
  /* Do not check online_flag. If there have several device on the bus,
   * we can use skip_rom to make all devices to start convert.
   * If check online_flag, it will be failed to start convert as the device
   * which we used to start convert not online. 
   */
  if(skip) {
    skip_rom();
  } else {
    match_rom();
  }
  uint8_t cmd = DS18B20_CMD_CONVERT_T;
  write(&cmd, 1);
}
/*
*/
void DS18B20::set_alarm(int8_t high, int8_t low)
{
  uint8_t buffer[9];
  //read
  read_scratchpad(buffer);  
  buffer[0] = high;
  buffer[1] = low;
  buffer[2] = buffer[4];
  buffer[3] = buffer[6];
  buffer[4] = buffer[7];
  //write back
  write_scratchpad(buffer);
}
/*
*/
void DS18B20::set_bits(uint8_t bits)
{
  uint8_t buffer[9];
  //read
  read_scratchpad(buffer);
  buffer[4] &= 0x03 << 5;
  if(bits == 12) {
    buffer[4] |= 0x03 << 5;
  } else if(bits == 11) {
    buffer[4] |= 0x02 << 5;
  } else if(bits == 10) {
    buffer[4] |= 0x01 << 5;
  } else {
    buffer[4] |= 0x00 << 5;
  }
  buffer[0] = buffer[2];
  buffer[1] = buffer[3];
  buffer[2] = buffer[4];
  buffer[3] = buffer[6];
  buffer[4] = buffer[7];
  //write back
  write_scratchpad(buffer);
}
/*
*/
int16_t DS18B20::temperature()
{
  int16_t temp;
  match_rom();
  uint8_t cmd = DS18B20_CMD_READ_SCRATCHPAD;
  write(&cmd, 1);
  read((uint8_t*)&temp, 2);  //just read 2 bits, and wait next reset to end this transmit
  if((uint16_t)temp == 0xffff) {
    online_flag = false;
    return TEMP_NONE;
  } else {
    online_flag = true;
    return temp * 100 / 16;
  }
}
/*
*/
bool DS18B20::check()
{
  uint8_t buffer[9];
  online_flag = true; //must make it online first
  //read first
  read_scratchpad(buffer);
  if(*(uint32_t*)buffer == 0xffffffff) {  //If not online, data read must be 0xffffffff
    online_flag = false;
  } else {
    online_flag = true;
  }
  
  return online_flag;
}
  
  
  